package com.xbongbong.pro.statistic.service.result;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.xbongbong.i18n.util.I18nMessageUtil;
import com.xbongbong.paas.constant.PaasConstant;
import com.xbongbong.paas.constant.SizeConstant;
import com.xbongbong.paas.domain.entity.PaasFormExplainEntity;
import com.xbongbong.paas.domain.entity.PaasStageEntity;
import com.xbongbong.paas.domain.entity.ext.PaasFormDataEntityExt;
import com.xbongbong.paas.elasticsearch.pojo.XbbAggregatedPage;
import com.xbongbong.paas.elasticsearch.util.EsUtil;
import com.xbongbong.paas.elasticsearch.util.XbbElasticsearchRestTemplate;
import com.xbongbong.paas.enums.DelEnum;
import com.xbongbong.paas.enums.FieldTypeEnum;
import com.xbongbong.paas.enums.FlowStatusEnum;
import com.xbongbong.paas.enums.IndexTypeEnum;
import com.xbongbong.paas.enums.StageTypeEnum;
import com.xbongbong.paas.field.FieldAttrEntity;
import com.xbongbong.paas.field.ItemPoJo;
import com.xbongbong.paas.model.PaasFormExplainModel;
import com.xbongbong.paas.pojo.StageFindPojo;
import com.xbongbong.paas.toolbox.exception.XbbException;
import com.xbongbong.paas.toolbox.util.Arith;
import com.xbongbong.paas.toolbox.util.BeanUtil;
import com.xbongbong.paas.toolbox.util.StringUtil;
import com.xbongbong.paas.util.ExplainUtil;
import com.xbongbong.pro.constant.CommonConstant;
import com.xbongbong.pro.constant.ElasticConstant;
import com.xbongbong.pro.domain.entity.ChartEntity;
import com.xbongbong.pro.enums.errorcodes.StatisticsServicesErrorCodeEnum;
import com.xbongbong.pro.service.stage.StageFindStrategy;
import com.xbongbong.pro.service.stage.factory.StageFindFactory;
import com.xbongbong.pro.service.toolbox.statistic.util.ChartResultUtil;
import com.xbongbong.pro.statistic.constant.ChartI18Constant;
import com.xbongbong.pro.statistic.enums.CLueConversionStageEnum;
import com.xbongbong.pro.statistic.enums.ChartTypeEnum;
import com.xbongbong.pro.statistic.enums.IndicatorRuleEnum;
import com.xbongbong.pro.statistic.enums.NumFormatEnum;
import com.xbongbong.pro.statistic.enums.SystemCodeEnum;
import com.xbongbong.pro.statistic.enums.ValueTypeEnum;
import com.xbongbong.pro.statistic.pojo.TimePojo;
import com.xbongbong.pro.statistic.result.pojo.AggsBucketsPojo;
import com.xbongbong.pro.statistic.result.pojo.ChartResultPkPojo;
import com.xbongbong.pro.statistic.result.pojo.ChartResultPojo;
import com.xbongbong.pro.statistic.result.pojo.ElasticAggsPojo;
import com.xbongbong.pro.statistic.result.pojo.PanoramaPojo;
import com.xbongbong.pro.statistic.result.pojo.StageFunnelInfoPojo;
import com.xbongbong.pro.statistic.result.pojo.StageFunnelPojo;
import com.xbongbong.pro.statistic.result.pojo.dto.StatisticResultDTO;
import com.xbongbong.pro.statistic.result.pojo.dto.StatisticResultInfoDTO;
import com.xbongbong.pro.statistic.result.pojo.dto.StatisticResultPieInfoDTO;
import com.xbongbong.pro.statistic.result.pojo.vo.ChartResultDetailVO;
import com.xbongbong.pro.statistic.result.pojo.vo.ChartResultPkDetailVO;
import com.xbongbong.pro.statistic.result.pojo.vo.ResultChartVO;
import com.xbongbong.pro.statistic.result.pojo.vo.ResultDataSeriesChartVO;
import com.xbongbong.pro.statistic.result.pojo.vo.ResultDataTableVO;
import com.xbongbong.pro.statistic.result.pojo.vo.ResultDoubleListVO;
import com.xbongbong.pro.statistic.result.pojo.vo.ResultTableVO;
import com.xbongbong.pro.statistic.through.pojo.dto.ThroughDTO;
import com.xbongbong.saas.constant.BasicConstant;
import com.xbongbong.saas.constant.StringConstant;
import com.xbongbong.saas.domain.entity.SaleStageEntity;
import com.xbongbong.saas.enums.ReportTargetEnum;
import com.xbongbong.saas.enums.XbbRefTypeEnum;
import com.xbongbong.saas.enums.business.ClueEnum;
import com.xbongbong.saas.enums.business.ContractEnum;
import com.xbongbong.saas.enums.business.CustomerCommunicateEnum;
import com.xbongbong.saas.enums.business.CustomerManagementEnum;
import com.xbongbong.saas.enums.business.SalesOpportunityEnum;
import com.xbongbong.saas.enums.dictionary.ClueStatusEnum;
import com.xbongbong.saas.enums.dictionary.DictionaryEnum;
import com.xbongbong.saas.model.ClueStageLogModel;
import com.xbongbong.saas.model.SaleStageModel;
import com.xbongbong.sys.domain.entity.UserEntity;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.aggregations.metrics.SumAggregationBuilder;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.sort.FieldSortBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.data.domain.PageRequest;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

import static org.elasticsearch.index.query.QueryBuilders.boolQuery;
import static org.elasticsearch.index.query.QueryBuilders.rangeQuery;
import static org.elasticsearch.index.query.QueryBuilders.termQuery;
import static org.elasticsearch.index.query.QueryBuilders.termsQuery;

/**
 * @author guohui.zhang
 * @Title: 线索相关统计
 * @date 2020/12/415:01
 */
@Component
public class MarketCLueResult extends ChartResultParentService implements ChartResultSystem, ChartResultPk,PanoramaResult {

    @Resource
    private ClueStageLogModel clueStageLogModel;
    @Resource
    private PaasFormExplainModel paasFormExplainModel;
    @Resource
    private XbbElasticsearchRestTemplate xbbElasticsearchRestTemplate;
    @Resource
    private SaleStageModel saleStageModel;

    @Override
    public Object getTarget(ChartResultPojo chartResultPojo, ChartResultDetailVO chartResultDetailVO) throws XbbException {
        SystemCodeEnum systemCodeEnum = chartResultPojo.getSystemCodeEnum();
        switch (systemCodeEnum){
             // 当前线索数 首页
            case CLUE_TOTAL_HOME:
                return clueTotalHome(chartResultPojo, chartResultDetailVO);
            // 新增线索分析 首页
            case CLUE_ADD_HOME:
                // return clueAddHome(chartResultPojo, chartResultDetailVO);
            // 新增线索数分析
            case CLUE_ADD_CHART:
                return clueAddChart(chartResultPojo, chartResultDetailVO);
             // 线索跟进 首页
            case CLUE_FOLLOW_HOME:
                return clueFollowHome(chartResultPojo, chartResultDetailVO);
            // 已转化线索数
            case CLUE_CONVERSION:
                return clueConversionHome(chartResultPojo, chartResultDetailVO);
            // 线索转化率
            case  CLUE_CONVERSION_RATE:
                return clueConversionRate(chartResultPojo, chartResultDetailVO);
            // 线索阶段漏斗
            case CLUE_STAGE:
                return clueStage(chartResultPojo, chartResultDetailVO);
            // 线索转化漏斗
            case CLUE_CONVERSION_CHART:
                return clueConversion(chartResultPojo, chartResultDetailVO);
            // 线索来源分析
            case CLUE_SOURCE:
                return clueSource(chartResultPojo, chartResultDetailVO);
            // 无效线索数
            case CLUE_INVALID_RESOURCE:
                return clueInvalidResource(chartResultPojo, chartResultDetailVO);
            default:
                break;
        }
        return null;
    }

    /**
     * 线索转化了
     * @param chartResultPojo
     * @param chartResultDetailVO
     * @return
     */
    private Object clueConversionRate(ChartResultPojo chartResultPojo, ChartResultDetailVO chartResultDetailVO) throws XbbException {
        ElasticAggsPojo activeRateAggsPojo = new ElasticAggsPojo();

        // 转化线索
        ElasticAggsPojo conversionAggsPojo = clueConversion2ElasticAggsPojo(chartResultPojo, ValueTypeEnum.VALUE_TYPE, true);

        // 当前线索
        ElasticAggsPojo totalElasticAggsPojo = clueTotal2ElasticAggsPojo(chartResultPojo, ValueTypeEnum.VALUE_TYPE, SystemCodeEnum.CLUE_TOTAL_HOME, 1);

        Integer active = StringUtil.toInt(conversionAggsPojo.getTotal(), 0);
        Integer total = StringUtil.toInt(totalElasticAggsPojo.getTotal(), 0);
        Double activeRate = (total == 0)? 0 : Arith.div(active, total) * 100;
        if (activeRate > 0) {
            activeRate = Arith.div(activeRate, 1, 2);
        }
        activeRateAggsPojo.setTotal(activeRate.toString() + "%");
        // ThroughDTO throughDto = new ThroughDTO(chartResultPojo, chartResultPojo.getSystemCodeEnum());
        //是否画入表格内 0否1是
        Integer tableFlag = 0;
        String legend = null;
        String unit = null;
        Boolean totalFlag = false;
        String valueTitle = null;
        return systemChartHelp.packageResult4One(activeRateAggsPojo, chartResultPojo, chartResultDetailVO, null, tableFlag, legend, unit, totalFlag, valueTitle);
    }

    /**
     * 已转化线索数
     * @param chartResultPojo
     * @param chartResultDetailVO
     * @return
     */
    private Object clueConversionHome(ChartResultPojo chartResultPojo, ChartResultDetailVO chartResultDetailVO) throws XbbException {
        ElasticAggsPojo elasticAggsPojo = clueConversion2ElasticAggsPojo(chartResultPojo, ValueTypeEnum.ARRAY_TYPE, true);
        ThroughDTO throughDto = new ThroughDTO(chartResultPojo, chartResultPojo.getSystemCodeEnum());
        //是否画入表格内 0否1是
        Integer tableFlag = 1;
        String legend = IndicatorRuleEnum.CLUE_CONVERSION.getName();
        String unit = systemChartHelp.getUnit(I18nMessageUtil.getMessage(CommonConstant.EACH));
        Boolean totalFlag = true;
        String valueTitle = IndicatorRuleEnum.CLUE_CONVERSION.getName();
        return systemChartHelp.packageResult4One(elasticAggsPojo, chartResultPojo, chartResultDetailVO, throughDto, tableFlag, legend, unit, totalFlag, valueTitle);
    }

    /**
     * 已线索转化
     * @param chartResultPojo
     * @param arrayType
     * @return
     */
    private ElasticAggsPojo clueConversion2ElasticAggsPojo(ChartResultPojo chartResultPojo, ValueTypeEnum arrayType, Boolean flag) throws XbbException {
        /*--------处理查询条件--------*/
        String fieldTime = "addTime";
        BoolQueryBuilder boolQueryBuilder = chartQueryHelp.clueQuery4Conversion(true, chartResultPojo, fieldTime, flag);
        chartQueryHelp.getChildTeam(chartResultPojo, boolQueryBuilder);
        ElasticAggsPojo elasticAggsPojo = systemChartHelp.getCount4Agg(chartResultPojo, arrayType, IndexTypeEnum.IDX_SAAS_CLUE, boolQueryBuilder, fieldTime);
        return elasticAggsPojo;
    }

    /**
     *  跟進綫索書
     * @param chartResultPojo
     * @param chartResultDetailVO
     * @return
     */
    private Object clueFollowHome(ChartResultPojo chartResultPojo, ChartResultDetailVO chartResultDetailVO) throws XbbException {
        // 獲取数组
        ElasticAggsPojo elasticAggsPojo = clueFollow2ElasticAggsPojo(chartResultPojo, ValueTypeEnum.ARRAY_TYPE);
        // 获取总值
        ElasticAggsPojo elasticAggsPojoTotal = clueFollow2ElasticAggsPojo(chartResultPojo, ValueTypeEnum.VALUE_TYPE);
        elasticAggsPojo.setTotal(elasticAggsPojoTotal.getTotal());
        ThroughDTO throughDTO = new ThroughDTO(chartResultPojo, chartResultPojo.getSystemCodeEnum());
        //是否画入表格内 0否1是
        Integer tableFlag = 1;
        String legend = ReportTargetEnum.FOLLOW_CLUE.getName();
        String unit = systemChartHelp.getUnit(I18nMessageUtil.getMessage(CommonConstant.EACH));
        Boolean totalFlag = true;
        String valueTitle = ReportTargetEnum.FOLLOW_CLUE.getName();
        return systemChartHelp.packageResult4One(elasticAggsPojo, chartResultPojo, chartResultDetailVO, throughDTO, tableFlag, legend, unit, totalFlag, valueTitle);
    }

    /**
     * 首页新增线索数
     * @param chartResultPojo
     * @param chartResultDetailVO
     * @return
     */
    private Object clueAddHome(ChartResultPojo chartResultPojo, ChartResultDetailVO chartResultDetailVO) {

        return null;
    }

    @Override
    public Object getValueTarget(ChartResultPojo chartResultPojo) throws XbbException {
        SystemCodeEnum systemCodeEnum = chartResultPojo.getSystemCodeEnum();
        switch (systemCodeEnum){
            // 当前线索数 简报看板
            case CLUE_TOTAL_BOARD:
                return clueTotalBoard(chartResultPojo);
                // 新增线索 简报看板
            case CLUE_ADD_BOARD:
                return clueAddBoard(chartResultPojo);
            // 新增线索 首頁
            case CLUE_ADD_HOME:
                // 线索跟进 简报看板
            case CLUE_FOLLOW_BOARD:
                return clueFollowBoard(chartResultPojo);
            // 线索跟进 首页
            case CLUE_FOLLOW_HOME:
                break;
            default:
                break;
        }
        return null;
    }

    private Object clueInvalidResource(ChartResultPojo chartResultPojo, ChartResultDetailVO chartResultDetailVO) throws XbbException {
        String corpid = chartResultPojo.getCorpid();
        BoolQueryBuilder boolQueryBuilder = boolQuery();
        chartQueryHelp.clueFunnelQuery(chartResultPojo, boolQueryBuilder ,true, null);
        chartQueryHelp.getChildTeam(chartResultPojo,boolQueryBuilder);
        String clueStage = ClueEnum.getEsAttr4Keyword(ClueEnum.CLUE_STATUS);
        boolQueryBuilder.filter(termQuery(clueStage, ClueStatusEnum.INVALID.getCode()));
        boolQueryBuilder.filter(termQuery(StringConstant.CORPID + StringConstant.POINT + StringConstant.KEY_WORD, corpid));
        boolQueryBuilder.filter(termQuery(StringConstant.DEL, DelEnum.NORMAL.getDel()));
        String invalid = ClueEnum.getEsAttr4Keyword(ClueEnum.INVALID_REASON);
        Long formId = chartResultPojo.getRefId();
        boolQueryBuilder.filter(termQuery("formId", formId));
        List<ItemPoJo> clueInvalidReason = dataDictionaryModel.getDictionaryList4Saas(DictionaryEnum.CLUE_STAGE_INVALID, corpid, formId);
        List<String> reason = new ArrayList<>();
        clueInvalidReason.forEach(item->reason.add(item.getValue().toString()));
        // 未知这个去掉,确认过的
        List<ResultDataSeriesChartVO> list1 = systemChartHelp.getDataDictionaryDataSeries(IndexTypeEnum.IDX_SAAS_CLUE, boolQueryBuilder, invalid, clueInvalidReason);
        List<ResultDataSeriesChartVO> list = new ArrayList<>();
        if (clueInvalidReason.size() != list1.size()){
            for (ResultDataSeriesChartVO resultDataSeriesChartVO : list1) {
                if (reason.contains(resultDataSeriesChartVO.getCode())){
                    list.add(resultDataSeriesChartVO);
                }
            }
        } else{
            list = list1;
        }
        String tableName =SystemCodeEnum.CLUE_INVALID_RESOURCE.getName();
        ThroughDTO throughDto = new ThroughDTO(chartResultPojo, chartResultPojo.getSystemCodeEnum());
        StatisticResultPieInfoDTO statisticResultPieInfoDto = new StatisticResultPieInfoDTO(tableName, list, throughDto);
        List<StatisticResultPieInfoDTO> pieInfoDtoList = Collections.singletonList(statisticResultPieInfoDto);
        return systemChartHelp.packageResult4Pie(tableName, pieInfoDtoList, chartResultPojo, chartResultDetailVO);
    }

    /**
     * 线索转化漏斗
     * @param chartResultPojo
     * @param chartResultDetailVO
     * @return
     */
    private Object clueConversion(ChartResultPojo chartResultPojo, ChartResultDetailVO chartResultDetailVO) throws XbbException {
        StageFunnelPojo funnelPojo = getStaticData4Clue(chartResultPojo);
        return packageConversionClue(chartResultPojo, chartResultDetailVO, funnelPojo);
    }

    /**
     *  封装漏斗表参数
     * @param chartResultPojo
     * @param chartResultDetailVO
     * @param funnelPojo
     * @return
     */
    private Object packageConversionClue(ChartResultPojo chartResultPojo, ChartResultDetailVO chartResultDetailVO, StageFunnelPojo funnelPojo) {
        Map<String, JSONObject> stageIdObjectMap = funnelPojo.getStageIdObjectMap();
        //声明图和表的信息VO
        List<ResultChartVO> resultChartVOList = new ArrayList<>();
        ResultChartVO resultChartVO = new ResultChartVO();
        List<ResultDataSeriesChartVO> dataSeriesList = new ArrayList<>();
        ResultTableVO resultTableVO = new ResultTableVO();
        List<String> title = Arrays.asList(String.format(I18nMessageUtil.getMessage(ChartI18Constant.CLUE_CONVERSION_TITLE), chartResultPojo.getUnit()).split(","));
        resultTableVO.setTitle(systemChartHelp.titleNumRight(title));
        List<List<ResultDataTableVO>> resultDataTableVOList = new ArrayList<>();
        ChartEntity chartEntity = chartResultPojo.getChartEntity();
        if (chartEntity != null) {
            BeanUtil.copyProperties(chartEntity, resultChartVO);
        }
        ThroughDTO throughDto = new ThroughDTO(chartResultPojo, chartResultPojo.getSystemCodeEnum());
        TimePojo timePojo = throughHelp.formatThroughTimeInfo(false, chartResultPojo.getFiscalYearTimePojo(), chartResultPojo.getStartTime(), chartResultPojo.getEndTime());
        List<CLueConversionStageEnum> allEnum = CLueConversionStageEnum.getAllEnum();
        int flagValue = 0;
        int i= 0;
        int average = (allEnum.size() == 0) ? 0 : 100 / allEnum.size();
        for (CLueConversionStageEnum cLueConversionStageEnum : allEnum) {
            String name ;
            JSONObject jsonObject = stageIdObjectMap.get(cLueConversionStageEnum.getCode()+"");
            if(Objects.isNull(jsonObject)){
                continue;
            }
            if (Objects.equals(cLueConversionStageEnum.getCode(), BasicConstant.ONE) || Objects.equals(cLueConversionStageEnum.getCode(), BasicConstant.TWO)){
                name = String.format(cLueConversionStageEnum.getNames(), jsonObject.getString("num"));
            } else {
                name = String.format(cLueConversionStageEnum.getNames(), jsonObject.getString("num"), jsonObject.getString("money"), chartResultPojo.getUnit());
            }
            int length;
            if (i == 0) {
                length = 100;
            } else if (i < allEnum.size()) {
                length = 100 - average * i;
            } else {
                length = 100 - flagValue;
            }
            i++;
            //置入穿透需要的阶段code
            throughDto.setDictionaryCodeThrough(String.valueOf(cLueConversionStageEnum.getCode()));
            String through = throughHelp.getThrough(throughDto, timePojo);
            ResultDataSeriesChartVO dataSeries = new ResultDataSeriesChartVO(name, cLueConversionStageEnum.getName(), String.valueOf(cLueConversionStageEnum.getCode()), String.valueOf(length));
            dataSeries.setThrough(through);
            dataSeriesList.add(dataSeries);
            ResultDataTableVO nameVo = new ResultDataTableVO(cLueConversionStageEnum.getName(), null);
            ResultDataTableVO numVo = new ResultDataTableVO(jsonObject.getString("num"), through);
            ResultDataTableVO conversionRateVo = new ResultDataTableVO(jsonObject.getString("conversionRate"), null);
            ResultDataTableVO targetNumVo = new ResultDataTableVO(jsonObject.getString("targetNum"), null);
            ResultDataTableVO moneyVo = new ResultDataTableVO(jsonObject.getString("money"), null);
            List<ResultDataTableVO> resultDataTableVOs = Arrays.asList(nameVo, numVo, conversionRateVo, targetNumVo,moneyVo);
            resultDataTableVOList.add(resultDataTableVOs);
        }
        resultChartVO.setDataSeries(dataSeriesList);
        resultChartVOList.add(resultChartVO);
        resultTableVO.setData(resultDataTableVOList);
        resultTableVO.setTableName(SystemCodeEnum.CLUE_CONVERSION.getName());
        chartResultDetailVO.setChart(resultChartVOList);
        chartResultDetailVO.setTable(resultTableVO);
        return chartResultDetailVO;
    }


    /**
     * 获取线索,客户,商机,赢单商机,成交订单统计数据
     * @param chartResultPojo
     * @return
     */
    private StageFunnelPojo getStaticData4Clue(ChartResultPojo chartResultPojo) throws XbbException {

        Integer clueNum = 0;
        Integer opportunityNum = 0;
        Integer opportunityClueNum = 0;
        BigDecimal opportunityMoney  = new BigDecimal(0);
        Integer customerNum =0;
        Integer customerClueNum =0;
        Integer winOpprotunityNum = 0;
        Integer winOpprotunityClueNum = 0;
        BigDecimal winOpprotunityMoney = new BigDecimal(0);
        Integer successOrderNum =0;
        Integer successOrderClueNum =0;
        BigDecimal successOrderMoney = new BigDecimal(0);
        Long formId = chartResultPojo.getRefId();
        Integer numFormat = chartResultPojo.getNumFormat();
        NumFormatEnum numFormatEnum = NumFormatEnum.getByCode(numFormat);

        // 分页查询对应的线索
        Integer page = 1;
        Integer pageSize = 1000;
        BoolQueryBuilder boolQueryBuilder = null;
        List<String> fieldList = null;
        String fieldTime= "addTime";
        Boolean flag = true;
        Long maxId = 0L;
        while (flag) {
            boolQueryBuilder = chartQueryHelp.boolQueryBuilder4Form(chartResultPojo, fieldTime, true);
            if (formId != null && formId > 0) {
                boolQueryBuilder.filter(termQuery("formId", formId));
            }
            boolQueryBuilder.filter(rangeQuery("id").gt(maxId));
            //无需审批和通过
            chartQueryHelp.getChildTeam(chartResultPojo, boolQueryBuilder);
            boolQueryBuilder.filter(termsQuery("flowStatus", FlowStatusEnum.getStatisticType()));
            fieldList = new ArrayList<>();
            fieldList.add(StringConstant.DATA_ID);
            chartQueryHelp.clueFunnelQuery(chartResultPojo, boolQueryBuilder, true, null);
            SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
            PageRequest pageRequest = EsUtil.setPage(sourceBuilder, page, pageSize);
            sourceBuilder.query(boolQueryBuilder);
            sourceBuilder.sort(new FieldSortBuilder("id").order(SortOrder.ASC));
            SearchRequest searchRequest = new SearchRequest(IndexTypeEnum.IDX_SAAS_CLUE.getIndex());
            searchRequest.source(sourceBuilder);
            XbbAggregatedPage<PaasFormDataEntityExt> esEntities = xbbElasticsearchRestTemplate.queryForPages(pageRequest, searchRequest, PaasFormDataEntityExt.class);

            if (esEntities.getContent().size() < pageSize) {
                flag = false;
            } else{
                maxId = esEntities.getContent().get(esEntities.getContent().size()-1).getId();
            }
            if(esEntities.getContent().size()==0){
                break;
            }
            clueNum += esEntities.getContent().size();
            List<Long> clueIds = new ArrayList<>();
            for (PaasFormDataEntityExt paasFormDataEntityExt : esEntities.getContent()) {
                clueIds.add(paasFormDataEntityExt.getDataId());
            }
            // 查询客户的数量
            // 客户(线索转化为客户) 需要排除回收站中的客户
            List<PaasFormDataEntityExt> customerList = chartQueryHelp.getClue2Customer(chartResultPojo, clueIds);
            // 记录线索与客户关系
            Map<Long, Integer> customer2Clue = new HashMap<>(customerList.size());

            List<Long> customerIds = new ArrayList<>();
            for (PaasFormDataEntityExt item : customerList) {
                Object o = item.getData().get(CustomerManagementEnum.CLUE_ID.getAttr());
                if (o instanceof JSONArray){
                    List<Long> jsonArray = JSON.parseArray(o.toString(), Long.class);
                    jsonArray.retainAll(clueIds);
                    customerClueNum += jsonArray.size();
                    customer2Clue.put(item.getDataId(), jsonArray.size() > 0 ? jsonArray.size() : 0);
                    customerIds.add(item.getDataId());
                } else if (o instanceof String){
                    customerClueNum++;
                    customer2Clue.put(item.getDataId(), BasicConstant.ONE);
                    customerIds.add(item.getDataId());
                }
            }
            customerNum += customerList.size();
            if (customerIds.size() > 0) {
                // 销售机会
                boolQueryBuilder = chartQueryHelp.getBaseClueConversion(chartResultPojo, customerIds, SalesOpportunityEnum.CUSTOMER_NAME.getAttr());
                String field = SalesOpportunityEnum.getEsAttr4Keyword(SalesOpportunityEnum.CUSTOMER_NAME);
                String fieldMoney = SalesOpportunityEnum.getEsAttr4Keyword(SalesOpportunityEnum.ESTIMATE_AMOUNT);
                boolQueryBuilder.filter(termsQuery(field, customerIds));
                TermsAggregationBuilder termsAggregationBuilder = AggregationBuilders.terms("aggs01").field(field);
                termsAggregationBuilder.subAggregation(new SumAggregationBuilder("totalMoney").field(fieldMoney));
                termsAggregationBuilder.size(Integer.MAX_VALUE);
                JSONObject resultJsonObject = esHelper.elasticAggs4Buckets2Object(IndexTypeEnum.IDX_SAAS_OPPORTUNITY, boolQueryBuilder, termsAggregationBuilder);
                JSONArray bucketsArray = resultJsonObject.getJSONArray("buckets");

                Set<Long> repeat = new HashSet<>();
                for (Object o : bucketsArray) {
                    JSONObject object = (JSONObject)o;
                    Long key = object.getLong("key");
                    opportunityMoney = getMoney(opportunityMoney, object);
                    Integer value = object.getInteger("doc_count");
                    if (!Objects.equals(value, BasicConstant.ZERO)){
                        if (!repeat.contains(key)) {
                            opportunityClueNum+=customer2Clue.get(key);
                        }
                        repeat.add(key);
                    }
                    opportunityNum+=value;
                }
                // 【获取所有成功阶段的id，用termsQuery查询阶段id字段】
//                String saleStage = SalesOpportunityEnum.getEsAttr4Keyword(SalesOpportunityEnum.SALE_STAGE);
//                boolQueryBuilder.filter(termQuery(saleStage, SaleStageEnum.DEAL.getCode()));
                String saleStageId = SalesOpportunityEnum.getEsAttr4Keyword(SalesOpportunityEnum.STAGE_ID);
                List<SaleStageEntity> saleStageList = saleStageModel.getList(chartResultPojo.getCorpid(), null, null, StageTypeEnum.SUCCESS_STAGE.getType());
                List<Long> saleStageIdList = saleStageList.stream().map(SaleStageEntity::getId).collect(Collectors.toList());
                boolQueryBuilder.filter(termsQuery(saleStageId, saleStageIdList));
                JSONObject resultWinOpp = esHelper.elasticAggs4Buckets2Object(IndexTypeEnum.IDX_SAAS_OPPORTUNITY, boolQueryBuilder, termsAggregationBuilder);
                JSONArray winOppJSONArray = resultWinOpp.getJSONArray("buckets");
                repeat.clear();
                for (Object o : winOppJSONArray) {
                    JSONObject object = (JSONObject)o;
                    Long key = object.getLong("key");
                    Integer value = object.getInteger("doc_count");
                    winOpprotunityMoney = getMoney(winOpprotunityMoney, object);
                    if (!Objects.equals(value, BasicConstant.ZERO)){
                        if (!repeat.contains(key)) {
                            winOpprotunityClueNum+=customer2Clue.get(key);
                        }
                        repeat.add(key);
                    }
                    winOpprotunityNum+=value;
                }

                // 合同
                boolQueryBuilder = chartQueryHelp.getBaseClueConversion(chartResultPojo, customerIds, ContractEnum.LINK_CUSTOMER.getAttr());
                field = ContractEnum.getEsAttr4Keyword(ContractEnum.LINK_CUSTOMER);
                fieldMoney = ContractEnum.getEsAttr4Keyword(ContractEnum.AMOUNT);
                boolQueryBuilder.filter(termsQuery(field, customerIds));
                TermsAggregationBuilder termsAggregationBuilder1 = AggregationBuilders.terms("aggs01").field(field);
                termsAggregationBuilder1.subAggregation(new SumAggregationBuilder("totalMoney").field(fieldMoney));
                termsAggregationBuilder1.size(Integer.MAX_VALUE);
                JSONObject resultContract = esHelper.elasticAggs4Buckets2Object(IndexTypeEnum.IDX_SAAS_CONTRACT, boolQueryBuilder, termsAggregationBuilder1);
                JSONArray contractJsonArray = resultContract.getJSONArray("buckets");
                repeat.clear();
                for (Object o : contractJsonArray) {
                    JSONObject object = (JSONObject)o;
                    Long key = object.getLong("key");
                    Integer value = object.getInteger("doc_count");
                    successOrderMoney = getMoney(successOrderMoney, object);
                    if (!Objects.equals(value, BasicConstant.ZERO)){
                        if (!repeat.contains(key)) {
                            successOrderClueNum+=customer2Clue.get(key);
                        }
                        repeat.add(key);
                    }
                    successOrderNum+=value;
                }
            }
        }
        Map<String, JSONObject> stageIdObjectMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);

        getConversionStage(clueNum, clueNum, stageIdObjectMap, "-", CLueConversionStageEnum.CLUE);
        getConversionStage(customerNum, customerClueNum, stageIdObjectMap, getConversionRate(clueNum, customerClueNum), CLueConversionStageEnum.CUSTOMER);


        JSONObject opportunityTotal = new JSONObject();
        opportunityTotal.put("num", opportunityClueNum);
        opportunityTotal.put("conversionRate", getConversionRate(clueNum, opportunityClueNum));
        opportunityTotal.put("targetNum", opportunityNum);
        opportunityTotal.put("money", ChartResultUtil.formatVal4Num(opportunityMoney, numFormatEnum));
        stageIdObjectMap.put(CLueConversionStageEnum.OPPORTUNITY.getCode().toString(), opportunityTotal);

        JSONObject winOpportunityTotal = new JSONObject();
        winOpportunityTotal.put("num", winOpprotunityClueNum);
        winOpportunityTotal.put("conversionRate", getConversionRate(clueNum, winOpprotunityClueNum));
        winOpportunityTotal.put("targetNum", winOpprotunityNum);
        winOpportunityTotal.put("money", ChartResultUtil.formatVal4Num(winOpprotunityMoney, numFormatEnum));
        stageIdObjectMap.put(CLueConversionStageEnum.WIN_OPPORTUNITY.getCode().toString(), winOpportunityTotal);


        JSONObject contractTotal = new JSONObject();
        contractTotal.put("num", successOrderClueNum);
        contractTotal.put("conversionRate", getConversionRate(clueNum, successOrderClueNum));
        contractTotal.put("targetNum", successOrderNum);
        contractTotal.put("money", ChartResultUtil.formatVal4Num(successOrderMoney, numFormatEnum));
        stageIdObjectMap.put(CLueConversionStageEnum.SUCCESS_ORDER.getCode().toString(), contractTotal);

        StageFunnelPojo stageFunnelPojo = new StageFunnelPojo(null, stageIdObjectMap);
        return stageFunnelPojo;
    }

    /**
     * 计算金额
     * @param money
     * @param object
     * @return
     */
    private BigDecimal getMoney(BigDecimal money, JSONObject object) {
        JSONObject totalMoney = object.getJSONObject("totalMoney");
        String value = totalMoney.getString("value");
        BigDecimal bigDecimal = new BigDecimal(value);
        return   money.add(bigDecimal).setScale(2, BigDecimal.ROUND_DOWN);
    }



    private void getConversionStage(Integer customerNum, Integer customerClueNum, Map<String, JSONObject> stageIdObjectMap, String conversionRate, CLueConversionStageEnum customer) {
        JSONObject customerTotal = new JSONObject();
        customerTotal.put("num", customerClueNum);
        customerTotal.put("conversionRate", conversionRate);
        customerTotal.put("targetNum", customerNum);
        customerTotal.put("money", "-");
        stageIdObjectMap.put(customer.getCode().toString(), customerTotal);
    }

    /**
     * 获取转化率
     * @param clueNum
     * @param customerClueNum
     * @return
     */
    public String getConversionRate(Integer clueNum, Integer customerClueNum) {
        String rate = "-";
        if (!Objects.equals(clueNum, BasicConstant.ZERO)){
            rate =Arith.div(customerClueNum*100d , clueNum, 2)+"%";
        }
        return rate;
    }

    private String getCompareRate(Double double1, Double customerClueNum) {
        String rate = "-";
        if (!Objects.equals(double1, BasicConstant.ZERO_DOUBLE)){
            rate =Arith.div(customerClueNum*100d , double1, 2)+"%";
        }
        return rate;
    }


    /**
     * 线索阶段漏斗
     * @param chartResultPojo
     * @param chartResultDetailVO
     * @return
     */
    private Object clueStage(ChartResultPojo chartResultPojo, ChartResultDetailVO chartResultDetailVO) throws XbbException {
        Long formId = chartResultPojo.getRefId();
         if (formId == null || formId == 0) {
             throw new XbbException(StatisticsServicesErrorCodeEnum.API_ERROR_224020, String.format(StatisticsServicesErrorCodeEnum.API_ERROR_224020.getMsg(), I18nMessageUtil.getMessage(CommonConstant.FORM)));
         }
        StageFunnelPojo funnelPojo = getFunnel4Clue(chartResultPojo);
        //阶段code--平均停留时长
        Map<String, Double> dayMap = getAverageStayTime(chartResultPojo, new ArrayList<>(funnelPojo.getCalcStayStageCodeIn()));

        return packageFunnelFull(chartResultPojo, chartResultDetailVO, funnelPojo.getSaleStageList(), funnelPojo, dayMap);
    }

    /**
     *  封装线索漏斗所需数据
     * @param chartResultPojo
     * @param chartResultDetailVO
     * @param saleStageList
     * @param funnelPojo
     * @param dayMap
     * @return
     */
    private Object packageFunnelFull(ChartResultPojo chartResultPojo, ChartResultDetailVO chartResultDetailVO, List<? extends PaasStageEntity> saleStageList, StageFunnelPojo funnelPojo, Map<String, Double> dayMap) {
        //阶段id--该阶段对应的数量
        Map<String, StageFunnelInfoPojo> stageIdInfoMap = funnelPojo.getStageIdInfoMap();
        //声明图和表的信息VO
        List<ResultChartVO> resultChartVOList = new ArrayList<>();
        ResultChartVO resultChartVO = new ResultChartVO();
        List<ResultDataSeriesChartVO> dataSeriesList = new ArrayList<>();
        ResultTableVO resultTableVO = new ResultTableVO();
        List<String> title = Arrays.asList(I18nMessageUtil.getMessage(ChartI18Constant.CLUE_STAGE_TITLE).split(","));
        resultTableVO.setTitle(systemChartHelp.titleNumRight(title));
        List<List<ResultDataTableVO>> resultDataTableVOList = new ArrayList<>();
        ChartEntity chartEntity = chartResultPojo.getChartEntity();
        if (chartEntity != null) {
            BeanUtil.copyProperties(chartEntity, resultChartVO);
        }
        int size = saleStageList.size();
        int average = (size == 0) ? 0 : 100 / size;
        int flagValue = 0;
        //该阶段后面所有阶段的总数（除去该阶段）
        int nextCount = funnelPojo.getTotal();
        //处理穿透
        ThroughDTO throughDto = new ThroughDTO(chartResultPojo, chartResultPojo.getSystemCodeEnum());
        TimePojo timePojo = throughHelp.formatThroughTimeInfo(false, chartResultPojo.getFiscalYearTimePojo(), chartResultPojo.getStartTime(), chartResultPojo.getEndTime());
        for (int i = 0; i < size; i++) {
            PaasStageEntity saleStageEntity = saleStageList.get(i);
            String realName = saleStageEntity.getName();
            String code = saleStageEntity.getCode();
            StageFunnelInfoPojo stageFunnelInfoPojo = stageIdInfoMap.get(code);
            int count = (stageFunnelInfoPojo == null)? 0: stageFunnelInfoPojo.getCount();
            /**
             处理阶段转化率（最后一个赢单阶段无需计算），以及阶段停留时长：除了该阶段以及该阶段前面的数，即该阶段后面所有阶段的总数
             最后一个赢单阶段无需计算转化率和停留时长
             */
            nextCount -= count;
            String name = realName;
            String changeRateStr = "-";
            String averageDayStr = "-";
            if (funnelPojo.getCalcStayStageCodeIn().contains(code)) {
                changeRateStr = "0%";
                //当前阶段以及该阶段后面的所有总数
                int nowAndNextAllCount = nextCount + count;
                if (nowAndNextAllCount > 0) {
                    // 转化率（转化到下一阶段的成功率） = 该阶段后面所有阶段机会总数（乘以100，前端显示直接拼“%”即可）/ 当前时间段内所有机会作为该阶段的转化率
                    changeRateStr = Arith.div(nextCount * 100d, nowAndNextAllCount, 2) + "%";
                }
                Double day = dayMap.get(code);
                averageDayStr = day == null ? "-" : String.valueOf(day);
                name += String.format(I18nMessageUtil.getMessage(ChartI18Constant.CLUE_STAGE_CONTENT_1), count, averageDayStr);
            } else {
                name += String.format(I18nMessageUtil.getMessage(ChartI18Constant.CLUE_STAGE_CONTENT_2), count);
            }
            int value;
            if (i == 0) {
                value = 100;
            } else if (i < size) {
                value = 100 - average * i;
            } else {
                value = 100 - flagValue;
            }
            flagValue = value;
            //置入穿透需要的阶段code
            throughDto.setDictionaryCodeThrough(String.valueOf(code));
            String through = throughHelp.getThrough(throughDto, timePojo);
            ResultDataSeriesChartVO dataSeries = new ResultDataSeriesChartVO(name, realName, String.valueOf(code), String.valueOf(value));
            dataSeries.setThrough(through);
            dataSeriesList.add(dataSeries);
            ResultDataTableVO nameVo = new ResultDataTableVO(realName, null);
            ResultDataTableVO countVo = new ResultDataTableVO(String.valueOf(count), through);
            ResultDataTableVO changeRateVo = new ResultDataTableVO(changeRateStr, null);
            ResultDataTableVO averageDayVo = new ResultDataTableVO(averageDayStr, null);
            List<ResultDataTableVO> resultDataTableVOs = Arrays.asList(nameVo, countVo, changeRateVo, averageDayVo);
            resultDataTableVOList.add(resultDataTableVOs);
        }
        resultChartVO.setDataSeries(dataSeriesList);
        resultChartVOList.add(resultChartVO);
        resultTableVO.setData(resultDataTableVOList);
        resultTableVO.setTableName(SystemCodeEnum.CLUE_STAGE.getName());
        chartResultDetailVO.setChart(resultChartVOList);
        chartResultDetailVO.setTable(resultTableVO);
        return chartResultDetailVO;
    }

    /**
     * 获取线索平均停留时长
     * @param chartResultPojo
     * @param saleStageIdIn
     * @return
     * @throws XbbException
     */
    private Map<String, Double> getAverageStayTime(ChartResultPojo chartResultPojo, List<String> saleStageIdIn) throws XbbException {
        String fieldTime = "addTime";
        //BoolQueryBuilder boolQueryBuilder = chartQueryHelp.customerQuery4Team(chartResultPojo, fieldTime, true);

        //BoolQueryBuilder boolQueryBuilder = chartQueryHelp.boolQueryBuilder4Form(chartResultPojo, fieldTime, true);
        /*--------处理查询条件--------*/
        Long formId = chartResultPojo.getRefId();
        BoolQueryBuilder boolQueryBuilder = chartQueryHelp.boolQueryBuilder4Base(chartResultPojo.getCorpid());
        if (formId != null && formId > 0) {
            boolQueryBuilder.filter(termQuery("formId", formId));
        }

        //无需审批和通过
        boolQueryBuilder.filter(termsQuery("flowStatus", FlowStatusEnum.getStatisticType()));
        chartQueryHelp.clueFunnelQuery(chartResultPojo, boolQueryBuilder, true, null);
        //数据量超限（SizeConstant.BI_MUITI_DATA_LIMIT），不处理
        long count = paasEsModel.count(boolQueryBuilder, IndexTypeEnum.IDX_SAAS_CLUE);
        if(count > SizeConstant.BI_MUITI_DATA_LIMIT){
            return new HashMap<>(BasicConstant.ONE);
        }
        List<String> fieldList = Arrays.asList(FieldTypeEnum.DATAID.getAlias());
        List<PaasFormDataEntityExt> list = esHelper.findByScroll(IndexTypeEnum.IDX_SAAS_CLUE, boolQueryBuilder, PaasFormDataEntityExt.class, fieldList);
        Set<Long> clueIdSet = new HashSet<>();
        for (PaasFormDataEntityExt entityExt: list){
            clueIdSet.add(entityExt.getDataId());
        }
        return clueStageLogModel.getAverageStayTime(chartResultPojo.getCorpid(), clueIdSet, saleStageIdIn);
    }

    /**
     * 按对应的值聚合，得到最终各个阶段的线索数
     * @param chartResultPojo   参数pojo
     * @return
     */
    private StageFunnelPojo getFunnel4Clue(ChartResultPojo chartResultPojo) throws XbbException {
        /*--------处理查询条件--------*/
        Long formId = chartResultPojo.getRefId();
        //获取特定的线索阶段（客户formId）
        StageFindStrategy stageFindStrategy = StageFindFactory.getStageFindStrategy(XbbRefTypeEnum.CLUE.getCode());
        List<? extends PaasStageEntity> saleStageList = stageFindStrategy.findStageListSpecial(chartResultPojo.getCorpid(), new StageFindPojo(formId, chartResultPojo.getArgTwoId()));
        Set<String> allStageCodeIn = new HashSet<>();
        //普通阶段code集合，结束阶段（成功、失败、取消）不用计算停留时长
        List<String> ordinaryCodeIn = new ArrayList<>();
        List<Integer> ordinaryTypeList = StageTypeEnum.getOrdinaryType();
        for (PaasStageEntity stage : saleStageList) {
            String code = stage.getCode();
            Integer type = stage.getType();
            allStageCodeIn.add(code);
            if (ordinaryTypeList.contains(type)) {
                ordinaryCodeIn.add(code);
            }
        }
        String fieldTime = "addTime";
        BoolQueryBuilder boolQueryBuilder = chartQueryHelp.boolQueryBuilder4Form(chartResultPojo, fieldTime, true);
        if (formId != null && formId > 0) {
            boolQueryBuilder.filter(termQuery("formId", formId));
        }
        chartQueryHelp.getChildTeam(chartResultPojo, boolQueryBuilder);
        //无需审批和通过
        boolQueryBuilder.filter(termsQuery("flowStatus", FlowStatusEnum.getStatisticType()));
        chartQueryHelp.clueFunnelQuery(chartResultPojo, boolQueryBuilder, true, null);

        //---------------按阶段
        String field = ClueEnum.getEsAttr4Keyword(ClueEnum.CLUE_STAGE);
        boolQueryBuilder.filter(termsQuery(field, allStageCodeIn));
        //一层聚合：按照阶段id聚合分组一层即可
        TermsAggregationBuilder termsAggregationBuilder = AggregationBuilders.terms("aggs01").field(field);
        termsAggregationBuilder.size(Integer.MAX_VALUE);
        JSONObject resultJsonObject = esHelper.elasticAggs4Buckets2Object(IndexTypeEnum.IDX_SAAS_CLUE, boolQueryBuilder, termsAggregationBuilder);
        JSONArray bucketsArray = resultJsonObject.getJSONArray("buckets");
        /**
         * 阶段id--该阶段对应的客户数
         */
        Map<String, StageFunnelInfoPojo> stageIdInfoMap = new HashMap<>(bucketsArray.size());
        //总数
        int total = 0;
        for (Object object : bucketsArray) {
            if(object!=null){
                JSONObject json = (JSONObject) object;
                String code = json.getString("key");
                Integer count = json.getInteger("doc_count");
                total += count;
                StageFunnelInfoPojo stageFunnelInfoPojo = new StageFunnelInfoPojo(count);
                stageIdInfoMap.put(code, stageFunnelInfoPojo);
            }
        }
        //只把非结束阶段的code放入集合
        StageFunnelPojo stageFunnelPojo = new StageFunnelPojo(total, stageIdInfoMap, saleStageList, ordinaryCodeIn);
        return stageFunnelPojo;
    }

    /**
     * 线索来源分析
     * @param chartResultPojo
     * @param chartResultDetailVO
     * @return
     */
    private Object clueSource(ChartResultPojo chartResultPojo, ChartResultDetailVO chartResultDetailVO) throws XbbException {
        String corpid = chartResultPojo.getCorpid();
        Long formId = chartResultPojo.getRefId();
        String fieldTime = "addTime";
        if (formId == null || formId == 0) {
            throw new XbbException(StatisticsServicesErrorCodeEnum.API_ERROR_224020, String.format(StatisticsServicesErrorCodeEnum.API_ERROR_224020.getMsg(), I18nMessageUtil.getMessage(CommonConstant.FORM)));
        }
        //获取特定的客户阶段（客户formId）
        PaasFormExplainEntity paasFormExplainEntity = paasFormExplainModel.getByFormId(formId, corpid);
        Map<String, FieldAttrEntity> explainMap = ExplainUtil.getExplainMap(paasFormExplainEntity.getExplains());
        FieldAttrEntity fieldAttrEntity = explainMap.get(ClueEnum.CLUE_SOURCE.getAttr());
        List<String> nameSpecial = new ArrayList<>();
        List<ItemPoJo> items = fieldAttrEntity.getItems();
        List<String> idIn = new ArrayList<>();
        items.forEach(item->{
            idIn.add(String.valueOf(item.getValue()));
            nameSpecial.add(item.getText());
        });
        ElasticAggsPojo addAggsPojo = getElasticAggsPojo(chartResultPojo, formId, false, idIn, fieldTime);
        ElasticAggsPojo conversionAggsPojo = getElasticAggsPojo(chartResultPojo, formId, true, idIn, fieldTime);
        ElasticAggsPojo converRateAggsPojo = getRate(addAggsPojo, conversionAggsPojo, items);
        List<String> legendList = Arrays.asList(I18nMessageUtil.getMessage(ChartI18Constant.CLUE_SOURCE_COLUMN).split(","));
        String unit = systemChartHelp.getUnit(I18nMessageUtil.getMessage(CommonConstant.EACH));
        String unit1 = systemChartHelp.getUnit("%");
        List<String> unitList = Arrays.asList(unit, unit, unit1);
        List<StatisticResultInfoDTO> infoDtos4MomYoy = getInfoDtos4MomYoy(chartResultPojo, legendList, unitList, addAggsPojo, conversionAggsPojo, converRateAggsPojo);

        chartResultPojo.setDateTitleList(nameSpecial);
        StatisticResultDTO statisticResultDto = new StatisticResultDTO(true, addAggsPojo.getTotal(), null, infoDtos4MomYoy);
        statisticResultDto.setTotalFlag(false);
        List<Integer> startTimeList = new ArrayList<>();
        List<Integer> endTimeList = new ArrayList<>();
        int size = chartResultPojo.getDateTitleList().size();
        for(int i=0; i<size; i++){
            startTimeList.add(chartResultPojo.getStartTime());
            endTimeList.add(chartResultPojo.getEndTime());
        }
        chartResultPojo.setStartTimeList(startTimeList);
        chartResultPojo.setEndTimeList(endTimeList);
        chartResultDetailVO = systemChartHelp.packageResult(statisticResultDto, chartResultPojo, chartResultDetailVO);
        return chartResultDetailVO;
    }

    /**
     * 获取线索来源转化率
     * @param addAggsPojo
     * @param conversionAggsPojo
     * @param items 线索来源字段
     * @return
     */
    private ElasticAggsPojo getRate(ElasticAggsPojo addAggsPojo, ElasticAggsPojo conversionAggsPojo, List<ItemPoJo> items) {
        // 新增线索数
        ResultDoubleListVO addListVO = systemChartHelp.getResultDoubleListVO(addAggsPojo);
        // 已转化线索数
        ResultDoubleListVO conversionVO = systemChartHelp.getResultDoubleListVO(conversionAggsPojo);

        List<Double> valueAddList = addListVO.getResultList();
        List<Double> valueConversionList = conversionVO.getResultList();


        //结果比率的数据
        ElasticAggsPojo elasticAggsPojo = new ElasticAggsPojo();
        //获取一次buckets
        List<AggsBucketsPojo> bucketsNow = addAggsPojo.getBuckets();

        //对桶内的值进行处理
        List<AggsBucketsPojo> rateList = elasticAggsPojo.getBuckets();
        for (int i = 0; i < valueAddList.size(); i++) {
            Double valueNow = valueAddList.get(i);
            Double valueLast = valueConversionList.get(i);
            String compareRate ;
            if (Objects.equals(valueNow, 0.0)){
                compareRate = "0.0 %";
            } else {
                compareRate = getCompareRate(valueNow, valueLast) ;
            }
            AggsBucketsPojo aggsBucketsPojo = bucketsNow.get(i);
            AggsBucketsPojo ratePojo = new AggsBucketsPojo(aggsBucketsPojo.getKey(), compareRate );
            rateList.add(ratePojo);
        }

        return elasticAggsPojo;
    }

    private List<StatisticResultInfoDTO> getInfoDtos4MomYoy(ChartResultPojo chartResultPojo, List<String> legendList, List<String> unitList, ElasticAggsPojo addAggsPojo, ElasticAggsPojo conversionAggsPojo, ElasticAggsPojo converRateAggsPojo) {

        //只有当月穿透，移动端产品销量不穿透
        // SystemCodeEnum systemCodeEnum = chartResultPojo.getSystemCodeEnum();
        // ThroughDTO throughDTO= new ThroughDTO(chartResultPojo, systemCodeEnum);
        //是否画入表格内 0否1是
        Integer tableFlag = 1;
        //分别采用两个不同单位的y轴，默认为yAxisIndex: 0，当为1时，代表另外一个y坐标轴
        Integer yAxisIndex = 0;
        //1隐藏，0不隐藏
        Integer hide = 0;
        //--------------------y轴左侧--------------------
        // 新增线索数
        // SystemCodeEnum systemCodeEnum = chartResultPojo.getSystemCodeEnum();
        // ThroughDTO throughDTO= new ThroughDTO(chartResultPojo, systemCodeEnum);
        StatisticResultInfoDTO addStatisticResultInfoDto = new StatisticResultInfoDTO(new ThroughDTO(chartResultPojo, SystemCodeEnum.CLUE_ADD_BOARD), tableFlag, yAxisIndex, hide, legendList.get(0), unitList.get(0), addAggsPojo);
        // 已转化线索数
        StatisticResultInfoDTO conversionStatisticResultInfoDto = new StatisticResultInfoDTO(new ThroughDTO(chartResultPojo, SystemCodeEnum.CLUE_CONVERSION), tableFlag, yAxisIndex, hide, legendList.get(1), unitList.get(1), conversionAggsPojo);

        // StatisticResultInfoDTO rateStatisticResultInfoDto = new StatisticResultInfoDTO(null, tableFlag, yAxisIndex, hide, legendList.get(1), unitList.get(1), converRateAggsPojo);
        yAxisIndex = 1;
        hide = 1;

        //环比、同比，必须强制置为折线类型
        StatisticResultInfoDTO rateStatisticResultInfoDto = new StatisticResultInfoDTO(null, tableFlag, yAxisIndex, hide, legendList.get(2), unitList.get(2), converRateAggsPojo);
        rateStatisticResultInfoDto.setChartType(ChartTypeEnum.LINE_CHART.getCode());

        return Arrays.asList(addStatisticResultInfoDto, conversionStatisticResultInfoDto, rateStatisticResultInfoDto);

    }

    private ElasticAggsPojo getElasticAggsPojo(ChartResultPojo chartResultPojo, Long formId, Boolean flag, List<String> idIn, String fieldTime) throws XbbException {
        BoolQueryBuilder boolQueryBuilder = chartQueryHelp.boolQueryBuilder4Form(chartResultPojo, fieldTime, true);
        if (formId != null && formId > 0) {
            boolQueryBuilder.filter(termQuery("formId", formId));
        }
        if (flag) {
            boolQueryBuilder.filter(termQuery(ClueEnum.getEsAttr4Keyword(ClueEnum.CLUE_STATUS), ClueStatusEnum.TRANSFORMED.getCode()));
        }
        //无需审批和通过
        boolQueryBuilder.filter(termsQuery("flowStatus", FlowStatusEnum.getStatisticType()));
        chartQueryHelp.clueFunnelQuery(chartResultPojo, boolQueryBuilder, true, null);
        chartQueryHelp.getChildTeam(chartResultPojo,boolQueryBuilder);
        //---------------按阶段
        String field = ClueEnum.getEsAttr4Keyword(ClueEnum.CLUE_SOURCE);
        // boolQueryBuilder.filter(termsQuery(field, saleStageIdIn));
        //一层聚合：按照阶段id聚合分组一层即可
        TermsAggregationBuilder termsAggregationBuilder = AggregationBuilders.terms("aggs01").field(field);
        termsAggregationBuilder.size(Integer.MAX_VALUE);
        JSONObject resultJsonObject = esHelper.elasticAggs4Buckets2Object(IndexTypeEnum.IDX_SAAS_CLUE, boolQueryBuilder, termsAggregationBuilder);
        JSONArray bucketsArray = resultJsonObject.getJSONArray("buckets");
        ElasticAggsPojo thisMonthAggsPojo = new ElasticAggsPojo();
        List<AggsBucketsPojo> list = new ArrayList<>();
        Map<String, JSONObject> bucketsList = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        List<String> bucketString = new ArrayList<>();
        for (Object o : bucketsArray) {
            JSONObject jsonObject = (JSONObject)o;
            bucketsList.put(jsonObject.getString("key"), jsonObject);
            bucketString.add(jsonObject.getString("key"));
        }
        Integer num =0;
        for (String s : idIn) {
            if (Objects.equals(s, "")){
                continue;
            }
            JSONObject jsonObject = bucketsList.get(s);
            if (Objects.nonNull(jsonObject)){
                list.add(new AggsBucketsPojo(jsonObject.getString("key"),jsonObject.getString("doc_count")));
            }else {
                list.add(new AggsBucketsPojo(s, "0"));
            }
        }
        for (String s : bucketString) {
            if (!idIn.contains(s)){
                JSONObject jsonObject = bucketsList.get(s);
                num+=jsonObject.getInteger("doc_count");
            }
        }
        if (idIn.contains("")){
            list.add(new AggsBucketsPojo("", num.toString()));
        }
        thisMonthAggsPojo.setBuckets(list);
        return thisMonthAggsPojo;
    }

    /**
     * 新增线索
     * @param chartResultPojo
     * @param chartResultDetailVO
     * @return
     * @throws XbbException
     */
    private Object clueAddChart(ChartResultPojo chartResultPojo, ChartResultDetailVO chartResultDetailVO) throws XbbException {
        ElasticAggsPojo elasticAggsPojo = clueAdd2ElasticAggsPojo(chartResultPojo, ValueTypeEnum.ARRAY_TYPE);
        ThroughDTO throughDto = new ThroughDTO(chartResultPojo, chartResultPojo.getSystemCodeEnum());
        //是否画入表格内 0否1是
        Integer tableFlag = 1;
        String legend = IndicatorRuleEnum.CLUE_ADD.getName();
        String unit = systemChartHelp.getUnit(I18nMessageUtil.getMessage(CommonConstant.EACH));
        Boolean totalFlag = true;
        String valueTitle = IndicatorRuleEnum.CLUE_ADD.getName();
        return systemChartHelp.packageResult4One(elasticAggsPojo, chartResultPojo, chartResultDetailVO, throughDto, tableFlag, legend, unit, totalFlag, valueTitle);
    }

    private ElasticAggsPojo clueAdd2ElasticAggsPojo(ChartResultPojo chartResultPojo, ValueTypeEnum arrayType) throws XbbException {
        /*--------处理查询条件--------*/
        String fieldTime = "addTime";
        BoolQueryBuilder boolQueryBuilder = chartQueryHelp.clueQuery4Create(true, chartResultPojo, fieldTime);

        ElasticAggsPojo elasticAggsPojo = systemChartHelp.getCount4Agg(chartResultPojo, arrayType, IndexTypeEnum.IDX_SAAS_CLUE, boolQueryBuilder, fieldTime);
        return elasticAggsPojo;
    }

    /**
     * 获取线索总数
     * @param chartResultPojo
     * @param chartResultDetailVO
     * @return
     */
    private Object clueTotalHome(ChartResultPojo chartResultPojo, ChartResultDetailVO chartResultDetailVO) throws XbbException {
        //未来时间是否计算 0否 1是，此处默认0不计算
        ElasticAggsPojo elasticAggsPojo = clueTotal2ElasticAggsPojo(chartResultPojo, ValueTypeEnum.VALUE_TYPE, chartResultPojo.getSystemCodeEnum(), 1);
        ThroughDTO throughDto = new ThroughDTO(chartResultPojo, chartResultPojo.getSystemCodeEnum());
        //是否画入表格内 0否1是
        Integer tableFlag = 0;
        String legend = null;
        String unit = null;
        Boolean totalFlag = false;
        String valueTitle = null;
        return systemChartHelp.packageResult4One(elasticAggsPojo, chartResultPojo, chartResultDetailVO, throughDto, tableFlag, legend, unit, totalFlag, valueTitle);
    }

    /**
     * 统计线索总数
     * @param chartResultPojo
     * @param valueTypeEnum
     * @param systemCodeEnum
     * @param futureCalc
     * @return
     */
    private ElasticAggsPojo clueTotal2ElasticAggsPojo(ChartResultPojo chartResultPojo, ValueTypeEnum valueTypeEnum, SystemCodeEnum systemCodeEnum, int futureCalc) throws XbbException {
        String fieldTime = "addTime";
        //不计算未来时间
        chartResultPojo.setFutureCalc(futureCalc);
        BoolQueryBuilder boolQueryBuilder = chartQueryHelp.clueQuery4Team(chartResultPojo, fieldTime, true);
        ElasticAggsPojo elasticAggsPojo = systemChartHelp.getCount4Agg(chartResultPojo, valueTypeEnum, IndexTypeEnum.IDX_SAAS_CLUE, boolQueryBuilder, fieldTime);
        /*--------再获取出截至到startTime的所有客户数--------*/
        //移除addTime，重新塞入新的条件
        esHelper.removeSearchCondition(boolQueryBuilder, fieldTime);
        int startTime = chartResultPojo.getStartTime();
        boolQueryBuilder.filter(rangeQuery(fieldTime).lt(startTime));
        Integer pastTotal = systemChartHelp.getCount4Total(IndexTypeEnum.IDX_SAAS_CLUE, boolQueryBuilder);
        //处理结果
        return ChartResultUtil.totalAggs(elasticAggsPojo, pastTotal, valueTypeEnum, chartResultPojo, futureCalc);
    }


    private String clueFollowBoard(ChartResultPojo chartResultPojo) throws XbbException {
        //ES获取跟进客户数
        ElasticAggsPojo elasticAggsPojo = clueFollow2ElasticAggsPojo(chartResultPojo, ValueTypeEnum.VALUE_TYPE);
        return elasticAggsPojo.getTotal();
    }

    private ElasticAggsPojo clueFollow2ElasticAggsPojo(ChartResultPojo chartResultPojo, ValueTypeEnum valueType) throws XbbException {
        BoolQueryBuilder boolQueryBuilder = chartQueryHelp.communicateQuery4UserLimitStart(chartResultPojo);
        chartQueryHelp.communicateFollowQuery(boolQueryBuilder, XbbRefTypeEnum.CLUE);
        String fieldTime = CustomerCommunicateEnum.getEsAttr4Keyword(CustomerCommunicateEnum.COMMUNICATE_TIME);
        //排重字段---客户id
        String fieldGroup = CustomerCommunicateEnum.getEsAttr4Keyword(CustomerCommunicateEnum.COMMUNICATE_BUSINESS);
        //用关联客户id聚合结果
        return systemChartHelp.distinctAggregations(chartResultPojo, valueType, IndexTypeEnum.IDX_SAAS_CUSTOMER_COMMUNICATE, boolQueryBuilder, fieldTime, fieldGroup);
    }

    /**
     * 新增客户数首页看板
     * @param chartResultPojo
     * @return
     */
    private Object clueAddBoard(ChartResultPojo chartResultPojo) throws XbbException {
        /*--------处理查询条件--------*/
        String fieldTime = "addTime";
        BoolQueryBuilder boolQueryBuilder = chartQueryHelp.clueQuery4Create(true, chartResultPojo, fieldTime);
        ElasticAggsPojo elasticAggsPojo = esHelper.elasticAggs(IndexTypeEnum.IDX_SAAS_CLUE, boolQueryBuilder, null);
        return elasticAggsPojo.getTotal();
    }

    /**
     * 當前客户总数(首页看板)
     * @param chartResultPojo
     * @return
     */
    private Object clueTotalBoard(ChartResultPojo chartResultPojo) throws XbbException {
        //未来时间是否计算 0否 1是，此处默认0不计算
        String fieldTime = "addTime";
        BoolQueryBuilder boolQueryBuilder = chartQueryHelp.clueQuery4Team(chartResultPojo, fieldTime, false);
        /*--------再获取出截至到startTime的所有客户数--------*/
        int count = systemChartHelp.getCount4Total(IndexTypeEnum.IDX_SAAS_CLUE, boolQueryBuilder);
        return String.valueOf(count);
    }

    @Override
    public Object customer(PanoramaPojo panoramaPojo) throws XbbException {
        return null;
    }

    @Override
    public Object getPkTarget(UserEntity userEntity, ChartResultPkPojo chartResultPkPojo, ChartResultPkDetailVO chartResultPkDetailVO) throws XbbException {
        return null;
    }

    @Override
    public Object getPkRuleTarget(UserEntity userEntity, ChartEntity chartEntity, ChartResultPkPojo chartResultPkPojo, ChartResultPkDetailVO chartResultPkDetailVO) throws XbbException {
        SystemCodeEnum systemCodeEnum = chartResultPkPojo.getSystemCodeEnum();
        switch (systemCodeEnum) {
            //pk、完成情况（员工排行）
            case PK_CLUE_CONVERSION_RATE:
                return pkRuleClueAdd(userEntity, chartEntity, chartResultPkPojo, chartResultPkDetailVO);
            default:
                break;
        }
        return null;
    }

    private Object pkRuleClueAdd(UserEntity userEntity, ChartEntity chartEntity, ChartResultPkPojo chartResultPkPojo, ChartResultPkDetailVO chartResultPkDetailVO) throws XbbException {
        String fieldTime = "addTime";
        /*--------处理查询条件--------*/
        BoolQueryBuilder boolQueryBuilder = chartRuleQueryHelp.boolQueryBuilder4LimitStart(chartResultPkPojo, fieldTime);
        TermsAggregationBuilder termsAggregationBuilder = AggregationBuilders.terms("aggs01").field("creatorId.keyword");
        termsAggregationBuilder.executionHint(ElasticConstant.EXECUTION_HINT);
        int pkType = chartResultPkPojo.getPkType();
        return pkRuleHelp.getPkResult4Count(userEntity, chartEntity, chartResultPkPojo, chartResultPkDetailVO, IndexTypeEnum.IDX_SAAS_CLUE, boolQueryBuilder, termsAggregationBuilder, pkType);
    }
}
